using System;
using System.Collections.Generic;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Close.io.Client
{
	public class CloseIoHelper<T> where T : IIdentifiable
	{

		public T Get(string id)
		{
			if (id == null) throw new ArgumentNullException("id");

			var typeName = typeof(T).Name;
			var endPoint = CloseIoEndpoints.GetEndpoint(typeName);
			var client = new CloseIoClient();
			CloseIoApiResult result = Retry.Do(client.Get, endPoint, id, TimeSpan.FromSeconds(1));
			if (result.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(result.ReasonPhrase);
			}
			var obj = JsonConvert.DeserializeObject<T>(result.Content);
			return obj;
		}

		public IEnumerable<T> Get(Dictionary<string, string> queryParameters)
		{
			if (queryParameters == null) throw new ArgumentNullException("queryParameters");

			var typeName = typeof(T).Name;
			var endPoint = CloseIoEndpoints.GetEndpoint(typeName);
			var client = new CloseIoClient();
			CloseIoApiResult result = Retry.Do(client.Get, endPoint, queryParameters, TimeSpan.FromSeconds(1));
			if (result.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(result.ReasonPhrase);
			}

			var objects = result.ContentObject["data"].ToObject<IEnumerable<T>>();

			return objects;
		}

		public void Add(string json)
		{
			var client = new CloseIoClient();
			var typeName = typeof(T).Name;
			var endPoint = CloseIoEndpoints.GetEndpoint(typeName);
			CloseIoApiResult result = Retry.Do(client.Post, endPoint, json, TimeSpan.FromSeconds(1));
			if (result.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(result.ReasonPhrase);
			}
		
		}

		public T Add(T obj)
		{
			var client = new CloseIoClient();
			var typeName = typeof(T).Name;
			var endPoint = CloseIoEndpoints.GetEndpoint(typeName);
			var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
			string payload = JsonConvert.SerializeObject(obj, settings);
			CloseIoApiResult result = Retry.Do(client.Post, endPoint, payload, TimeSpan.FromSeconds(1));
			if (result.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(result.ReasonPhrase);
			}
			var resultObject = JsonConvert.DeserializeObject<T>(result.Content);
			return resultObject;
		}


		public void Delete(string id)
		{
			var typeName = typeof(T).Name;
			var endPoint = CloseIoEndpoints.GetEndpoint(typeName);
			var client = new CloseIoClient();
			CloseIoApiResult result = Retry.Do(client.Delete, endPoint, id, TimeSpan.FromSeconds(1));
			if (result.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(result.ReasonPhrase);
			}
		}

		public T Update(T obj)
		{

			var typeName = typeof(T).Name;
			var endPoint = CloseIoEndpoints.GetEndpoint(typeName);
			var client = new CloseIoClient();
			var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
			string payload = JsonConvert.SerializeObject(obj, settings);
			CloseIoApiResult result = Retry.Do(client.Put, endPoint, obj.Id, payload, TimeSpan.FromSeconds(1));
			if (result.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(result.ReasonPhrase);
			}
			var resultObject = JsonConvert.DeserializeObject<T>(result.Content);
			return resultObject;
		}

		public T Update(string id, string updateJson)
		{

			var typeName = typeof(T).Name;
			var endPoint = CloseIoEndpoints.GetEndpoint(typeName);
			var client = new CloseIoClient();
			CloseIoApiResult result = Retry.Do(client.Put, endPoint, id, updateJson, TimeSpan.FromSeconds(1));
			if (result.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(result.ReasonPhrase);
			}
			var resultObject = JsonConvert.DeserializeObject<T>(result.Content);
			return resultObject;
		}



		public T Update(string id, string endPoint, IEnumerable<JProperty> properties)
		{
			var payload = new JObject();
			foreach (JProperty property in properties)
			{
				payload.Add(property);
			}

			return Update(id, payload.ToString());

		}

	}
}